package data;

import java.io.IOException;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;

import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;

import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.w3c.tidy.Tidy;

/**
 * 
 * @author Maciej Donajski
 */
public class WebSiteLoader extends SocketLoader {

	/**
	 * Creates new instance of WebSiteLoader. <br />
	 * WebSiteLoader is used to acquire stock data from provided website. For proper usage one must specify {@link XPath xpaths} collection collection,
	 *  
	 * @param address
	 * @param xpaths
	 * @throws IOException
	 */
	public WebSiteLoader(URL address, Collection<RecordExpression> xpaths, DOMReader reader) throws IOException {
		super(address.getHost(), PORT);
		initialize(address);
		
		this.mXPaths = xpaths;
	}
	
	public WebSiteLoader(URL address) throws UnknownHostException, IOException {
		super(address.getHost(), PORT);
		initialize(address);
	}
	
	public Record load(RecordExpression xpaths) {
		
		return null;
	}

	public Collection<Record> load(Collection<RecordExpression> xpaths) throws Exception {
		Collection<Record> r = new ArrayList<Record>();
		for(RecordExpression xp : xpaths) {
			r.add(this.readRecord(xp));
		}
		
		return r;
	}
	
	@Override
	public Collection<Record> load() throws Exception {
		return this.load(this.mXPaths);
	}
	
	private Record readRecord(RecordExpression xp) throws Exception {
		HashMap<String, NodeList> context = new HashMap<String, NodeList>();
		
		context.put(Record.NAME, 
				(NodeList)xp.getName().evaluate(this.mWebSite, XPathConstants.NODESET));
		context.put(Record.SYMBOL, 
				(NodeList)xp.getSymbol().evaluate(this.mWebSite, XPathConstants.NODESET));
		context.put(Record.MIN, 
				(NodeList)xp.getMinRate().evaluate(this.mWebSite, XPathConstants.NODESET));
		context.put(Record.MAX, 
				(NodeList)xp.getMaxRate().evaluate(this.mWebSite, XPathConstants.NODESET));
		context.put(Record.OPEN, 
				(NodeList)xp.getOpeningRate().evaluate(this.mWebSite, XPathConstants.NODESET));
		context.put(Record.CLOSE, 
				(NodeList)xp.getClosingRate().evaluate(this.mWebSite, XPathConstants.NODESET));
		context.put(Record.CURR, 
				(NodeList)xp.getCurrentRate().evaluate(this.mWebSite, XPathConstants.NODESET));
		context.put(Record.DATE, 
				(NodeList)xp.getTimeStamp().evaluate(this.mWebSite, XPathConstants.NODESET));
		
		return this.mReader.read(context);
	}

	private void initialize(URL address) throws IOException {
		Tidy tidy = new Tidy();
		tidy.setShowWarnings(false);
		tidy.setShowErrors(0);
		tidy.setQuiet(true);
		tidy.setMakeClean(true);
		
		this.requestWebsite(address);

		this.mWebSite = tidy.parseDOM(this.getInputStream(), null);	
		
		if(mWebSite == null)
			throw new IOException(ERR_01);
	}
	
	private void requestWebsite(URL url) throws IOException {
		this.write("GET " + url.getFile() + " " + HTTP_V + "\r\n");
		this.write("Host: " + url.getHost() + "\r\n\r\n");
		if(!this.readLine().endsWith(HTTP_OK))
			throw new IOException("Get request failed");
		//skip to html content
		while(!this.readLine().isEmpty()) continue;
	}
	
	// Members
	/** parsed website */
	private Document mWebSite;
	
	/** WebSite reader, capable of reading data out from Document object */
	private Collection<RecordExpression> mXPaths;
	
	/** data reader */
	private DOMReader mReader;
	
	
	//Consts
	
	/** http port */
	private static final int 	PORT   = 80;
	
	/** http version */
	private static final String HTTP_V = "HTTP/1.0";
	
	/** http ok code */
	private static final String HTTP_OK = "200 OK";
	
	
	//errors
	
	private static final String ERR_01 = "DOM parsing error";
	
}
